package com.cps.frameloadbalance.util;

import com.alibaba.fastjson.JSONObject;
import com.cps.framecommon.domain.vo.WeightInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;

/**
 * @author 11202
 */
@Service
@Slf4j
public class ReadWeight {
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    private final Object syncLock = new Object();

    /**
     * 最长容忍时间
     */
    public static final int MAX_TOLERATE_TIME = 5 * 1000;

    private static final ConcurrentHashMap<String, WeightInfo> weightMap = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, Timestamp> LOAD_TIME_MAP = new ConcurrentHashMap<>();

    public boolean isThresholdExceeded(String serviceName) {
        syncServiceWeightInfo(serviceName);
        return Optional.of(weightMap)
                .map(weightInfo ->weightInfo.get(serviceName))
                .map(WeightInfo::getThresholdExceeded)
                .orElse(false);
    }

    public Double getServiceWeight(ServiceInstance serviceInstance) {
        String serviceName = serviceInstance.getServiceId();
        syncServiceWeightInfo(serviceName);
        String serviceAddress = serviceInstance.getHost() + "_" + serviceInstance.getPort();
        return Optional.of(weightMap)
                .map(weightInfo ->weightInfo.get(serviceName))
                .map(WeightInfo::getWeightMap)
                .map(weightMap ->weightMap.get(serviceAddress))
                .orElse(null);
    }

    private synchronized void syncServiceWeightInfo(String serviceName) {
        if (!LOAD_TIME_MAP.containsKey(serviceName) || LOAD_TIME_MAP.get(serviceName).getTime() + MAX_TOLERATE_TIME < System.currentTimeMillis()) {
            synchronized (syncLock) {
                if (!LOAD_TIME_MAP.containsKey(serviceName) || LOAD_TIME_MAP.get(serviceName).getTime() + MAX_TOLERATE_TIME < System.currentTimeMillis()) {
                    String jsonStr = redisTemplate.opsForValue().get("service_weight:" + serviceName);
                    if (jsonStr == null || jsonStr.isEmpty()) {
                        weightMap.put(serviceName, new WeightInfo());
                    } else {
                        weightMap.put(serviceName, JSONObject.parseObject(jsonStr, WeightInfo.class));
                    }
                    LOAD_TIME_MAP.put(serviceName, new Timestamp(System.currentTimeMillis()));
                }
            }
        }

    }

}
